package org.smartboot.ioc.transport;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Future;

/**
 * @author 三刀
 * @version V1.0 , 2018/5/24
 */
class AsynchronousSocketChannel {

    private ByteBuffer readBuffer;
    private ByteBuffer writeBuffer;
    private CompletionHandler<Integer, Object> readCompletionHandler;
    private CompletionHandler<Integer, Object> writeCompletionHandler;
    private Object readAttachment;
    private Object writeAttachment;
    private SelectionKey selectionKey;

    private SocketChannel channel;

    public AsynchronousSocketChannel(SelectionKey selectionKey) {
        this.selectionKey = selectionKey;
        this.channel = (SocketChannel) selectionKey.channel();
    }

    public final <A> void read(ByteBuffer dst,
                               A attachment,
                               CompletionHandler<Integer, ? super A> handler) {
        this.readBuffer = dst;
        this.readAttachment = attachment;
        this.readCompletionHandler = (CompletionHandler<Integer, Object>) handler;
        selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_READ);
        selectionKey.selector().wakeup();
    }

    public final <A> void write(ByteBuffer src,
                                A attachment,
                                CompletionHandler<Integer, ? super A> handler) {

        this.writeBuffer = src;
        this.writeAttachment = attachment;
        this.writeCompletionHandler = (CompletionHandler<Integer, Object>) handler;
        selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_WRITE);
        selectionKey.selector().wakeup();
    }

    public void close() throws IOException {
        channel.close();
        selectionKey.cancel();
    }

    public Future<Integer> read(ByteBuffer readBuffer) {
        throw new UnsupportedOperationException();
    }

    public void doRead() {
        try {
            int readSize = channel.read(readBuffer);
            readCompletionHandler.completed(readSize, readAttachment);
        } catch (IOException e) {
            e.printStackTrace();
            readCompletionHandler.failed(e, readAttachment);
        }
    }

    public void doWrite() {
        try {
            int writeSize = channel.write(writeBuffer);
            writeCompletionHandler.completed(writeSize, writeAttachment);
        } catch (IOException e) {
            e.printStackTrace();
            writeCompletionHandler.failed(e, writeAttachment);
        }
    }
}
